﻿using System;
using System.Threading;
using System.Threading.Tasks;

namespace Beaker.Threading
{
	/// <summary>
	/// Extensions that can run functions synchronous while outputting it as a Task.
	/// </summary>
	public static class TaskCompletionSourceExtensions
	{
		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="cancellationToken">The cancellation token to signal cancelation of the task.</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TResult>(this TaskCompletionSource<TResult> source, Func<TResult> functionToRun, CancellationToken cancellationToken)
		{
			if (cancellationToken.IsCancellationRequested)
				source.SetCanceled();
			else
			{
				try
				{
					TResult result = functionToRun();

					if (cancellationToken.IsCancellationRequested)
						source.SetCanceled();
					else
						source.SetResult(result);
				}
				catch (Exception exception)
				{
					source.SetException(exception);
				}
			}

			return source.Task;
		}

		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TResult>(this TaskCompletionSource<TResult> source, Func<TResult> functionToRun)
		{
			return source.RunSynchronous(functionToRun, CancellationToken.None);
		}

		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <param name="cancellationToken">The cancellation token to signal cancelation of the task.</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TResult> functionToRun, TArg1 arg1, CancellationToken cancellationToken)
		{
			return source.RunSynchronous(() => functionToRun(arg1), cancellationToken);
		}

		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TResult> functionToRun, TArg1 arg1)
		{
			return source.RunSynchronous(() => functionToRun(arg1), CancellationToken.None);
		}
		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <param name="arg2">The second argument for the method that will return the result for the task</param>
		/// <param name="cancellationToken">The cancellation token to signal cancelation of the task.</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TArg2, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TArg2, TResult> functionToRun, TArg1 arg1, TArg2 arg2, CancellationToken cancellationToken)
		{
			return source.RunSynchronous(() => functionToRun(arg1, arg2), cancellationToken);
		}

		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <param name="arg2">The second argument for the method that will return the result for the task</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TArg2, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TArg2, TResult> functionToRun, TArg1 arg1, TArg2 arg2)
		{
			return source.RunSynchronous(() => functionToRun(arg1, arg2), CancellationToken.None);
		}
		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <param name="arg2">The second argument for the method that will return the result for the task</param>
		/// <param name="arg3">The thirth argument for the method that will return the result for the task</param>
		/// <param name="cancellationToken">The cancellation token to signal cancelation of the task.</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TArg2, TArg3, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TArg2, TArg3, TResult> functionToRun, TArg1 arg1, TArg2 arg2, TArg3 arg3, CancellationToken cancellationToken)
		{
			return source.RunSynchronous(() => functionToRun(arg1, arg2, arg3), cancellationToken);
		}

		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <param name="arg2">The second argument for the method that will return the result for the task</param>
		/// <param name="arg3">The thirth argument for the method that will return the result for the task</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TArg2, TArg3, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TArg2, TArg3, TResult> functionToRun, TArg1 arg1, TArg2 arg2, TArg3 arg3)
		{
			return source.RunSynchronous(() => functionToRun(arg1, arg2, arg3), CancellationToken.None);
		}
		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <param name="arg2">The second argument for the method that will return the result for the task</param>
		/// <param name="arg3">The thirth argument for the method that will return the result for the task</param>
		/// <param name="arg4">The fourth argument for the method that will return the result for the task</param>
		/// <param name="cancellationToken">The cancellation token to signal cancelation of the task.</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TArg2, TArg3, TArg4, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TArg2, TArg3, TArg4, TResult> functionToRun, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, CancellationToken cancellationToken)
		{
			return source.RunSynchronous(() => functionToRun(arg1, arg2, arg3, arg4), cancellationToken);
		}

		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <param name="arg2">The second argument for the method that will return the result for the task</param>
		/// <param name="arg3">The thirth argument for the method that will return the result for the task</param>
		/// <param name="arg4">The fourth argument for the method that will return the result for the task</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TArg2, TArg3, TArg4, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TArg2, TArg3, TArg4, TResult> functionToRun, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
		{
			return source.RunSynchronous(() => functionToRun(arg1, arg2, arg3, arg4), CancellationToken.None);
		}
		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <param name="arg2">The second argument for the method that will return the result for the task</param>
		/// <param name="arg3">The thirth argument for the method that will return the result for the task</param>
		/// <param name="arg4">The fourth argument for the method that will return the result for the task</param>
		/// <param name="arg5">The fifth argument for the method that will return the result for the task</param>
		/// <param name="cancellationToken">The cancellation token to signal cancelation of the task.</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TArg2, TArg3, TArg4, TArg5, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TArg2, TArg3, TArg4, TArg5, TResult> functionToRun, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, CancellationToken cancellationToken)
		{
			return source.RunSynchronous(() => functionToRun(arg1, arg2, arg3, arg4, arg5), cancellationToken);
		}

		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <param name="arg2">The second argument for the method that will return the result for the task</param>
		/// <param name="arg3">The thirth argument for the method that will return the result for the task</param>
		/// <param name="arg4">The fourth argument for the method that will return the result for the task</param>
		/// <param name="arg5">The fifth argument for the method that will return the result for the task</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TArg2, TArg3, TArg4, TArg5, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TArg2, TArg3, TArg4, TArg5, TResult> functionToRun, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5)
		{
			return source.RunSynchronous(() => functionToRun(arg1, arg2, arg3, arg4, arg5), CancellationToken.None);
		}
		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <param name="arg2">The second argument for the method that will return the result for the task</param>
		/// <param name="arg3">The thirth argument for the method that will return the result for the task</param>
		/// <param name="arg4">The fourth argument for the method that will return the result for the task</param>
		/// <param name="arg5">The fifth argument for the method that will return the result for the task</param>
		/// <param name="arg6">The sixth argument for the method that will return the result for the task</param>
		/// <param name="cancellationToken">The cancellation token to signal cancelation of the task.</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult> functionToRun, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, CancellationToken cancellationToken)
		{
			return source.RunSynchronous(() => functionToRun(arg1, arg2, arg3, arg4, arg5, arg6), cancellationToken);
		}

		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <param name="arg2">The second argument for the method that will return the result for the task</param>
		/// <param name="arg3">The thirth argument for the method that will return the result for the task</param>
		/// <param name="arg4">The fourth argument for the method that will return the result for the task</param>
		/// <param name="arg5">The fifth argument for the method that will return the result for the task</param>
		/// <param name="arg6">The sixth argument for the method that will return the result for the task</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult> functionToRun, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6)
		{
			return source.RunSynchronous(() => functionToRun(arg1, arg2, arg3, arg4, arg5, arg6), CancellationToken.None);
		}
		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <param name="arg2">The second argument for the method that will return the result for the task</param>
		/// <param name="arg3">The thirth argument for the method that will return the result for the task</param>
		/// <param name="arg4">The fourth argument for the method that will return the result for the task</param>
		/// <param name="arg5">The fifth argument for the method that will return the result for the task</param>
		/// <param name="arg6">The sixth argument for the method that will return the result for the task</param>
		/// <param name="arg7">The seventh argument for the method that will return the result for the task</param>
		/// <param name="cancellationToken">The cancellation token to signal cancelation of the task.</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult> functionToRun, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7, CancellationToken cancellationToken)
		{
			return source.RunSynchronous(() => functionToRun(arg1, arg2, arg3, arg4, arg5, arg6, arg7), cancellationToken);
		}

		/// <summary>
		/// Runs the given method and stores the result in the TaskCompletionSource
		/// </summary>
		/// <param name="source">The task completion source to set the result of the function on.</param>
		/// <param name="functionToRun">The function that will return the result for the task completion source</param>
		/// <param name="arg1">The first argument for the method that will return the result for the task</param>
		/// <param name="arg2">The second argument for the method that will return the result for the task</param>
		/// <param name="arg3">The thirth argument for the method that will return the result for the task</param>
		/// <param name="arg4">The fourth argument for the method that will return the result for the task</param>
		/// <param name="arg5">The fifth argument for the method that will return the result for the task</param>
		/// <param name="arg6">The sixth argument for the method that will return the result for the task</param>
		/// <param name="arg7">The seventh argument for the method that will return the result for the task</param>
		/// <remarks>
		/// If the method returns a result, that result is stored in the task completion source.
		/// If the method throws an exception, the exception is stored in the task completion source.
		/// If the task was canceled before the result was available, the task will be set to canceled.
		/// </remarks>
		public static Task<TResult> RunSynchronous<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult>(this TaskCompletionSource<TResult> source, Func<TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult> functionToRun, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5, TArg6 arg6, TArg7 arg7)
		{
			return source.RunSynchronous(() => functionToRun(arg1, arg2, arg3, arg4, arg5, arg6, arg7), CancellationToken.None);
		}
	}
}
